﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Xml.Linq;
using System.Xml.Serialization;
using LiveTiss.LiveTissWCF;

namespace LiveTiss.Classes
{
    public static class Util
    {
        #region Organizar depois
        public static readonly DateTime EmptyDate = new DateTime(0001, 01, 01, 0, 0, 0);

        public static void SetValidation(this FrameworkElement frameworkElement, string message)
        {
            CustomValidation customValidation = new CustomValidation(message);
            Binding binding = new Binding("ValidationError")
            {
                Mode = System.Windows.Data.BindingMode.TwoWay,
                NotifyOnValidationError = true,
                ValidatesOnExceptions = true,
                Source = customValidation
            };
            frameworkElement.SetBinding(Control.TagProperty, binding);
        }

        public static void RaiseValidationError(this FrameworkElement frameworkElement)
        {
            BindingExpression b = frameworkElement.GetBindingExpression(Control.TagProperty);
            if (b != null)
            {
                ((CustomValidation)b.DataItem).ShowErrorMessage = true;
                b.UpdateSource();
            }
        }

        public static void ClearValidationError(this FrameworkElement frameworkElement)
        {
            BindingExpression b =
            frameworkElement.GetBindingExpression(Control.TagProperty);
            if (b != null)
            {
                ((CustomValidation)b.DataItem).ShowErrorMessage = false;
                b.UpdateSource();
            }
        }

        public static List<KeyValuePair<string, string>> LoadXMLEnum(string xml)
        {
            List<KeyValuePair<string, string>> lista = new List<KeyValuePair<string, string>>();
            XDocument doc = XDocument.Load(xml);

            var keys = doc.Element("keyValuePair").Element("keys").Elements("key");
            foreach (var k in keys)
            {
                lista.Add(new KeyValuePair<string, string>(k.Attribute("value").Value, ""));
            }

            int i = 0;
            var values = doc.Element("keyValuePair").Element("values").Elements("value");
            foreach (var v in values)
            {
                lista[i] = new KeyValuePair<string, string>(lista[i].Key, v.Attribute("value").Value);
                i++;
            }
            return lista;
        }

        public static void LoadXMLEnum(string xml, System.Windows.Controls.Primitives.Selector container)
        {
            LoadXMLEnum(xml, container, -1);
        }

        public static void LoadXMLEnum(string xml, System.Windows.Controls.Primitives.Selector container, int selectedIndex)
        {
            WebClient xmlClient = new WebClient();

            xmlClient.DownloadStringCompleted += delegate(object sender, DownloadStringCompletedEventArgs e)
            {
                if (e.Error == null)
                {
                    List<KeyValuePair<string, string>> lista = new List<KeyValuePair<string, string>>();
                    XDocument doc = XDocument.Parse(e.Result);

                    var keys = doc.Element("keyValuePair").Element("keys").Elements("key");
                    foreach (var k in keys)
                    {
                        lista.Add(new KeyValuePair<string, string>(k.Attribute("value").Value, ""));
                    }

                    int i = 0;
                    var values = doc.Element("keyValuePair").Element("values").Elements("value");
                    foreach (var v in values)
                    {
                        lista[i] = new KeyValuePair<string, string>(lista[i].Key, v.Attribute("value").Value);
                        i++;
                    }
                    container.ItemsSource = lista;
                    container.SelectedIndex = selectedIndex;
                }
            };

            xmlClient.DownloadStringAsync(new Uri(xml, UriKind.RelativeOrAbsolute));
        }

        public static string SerializeObj(object obj)
        {
            XmlSerializer serializer = new XmlSerializer(obj.GetType());
            System.IO.MemoryStream ms = new System.IO.MemoryStream();

            serializer.Serialize(ms, obj);

            byte[] bytes = ms.ToArray();
            ms.Close();
            return System.Text.Encoding.UTF8.GetString(bytes, 0, bytes.Length);
        }

        public static string ConvertToString(Enum e)
        {
            // Get the Type of the enum
            Type t = e.GetType();

            // Get the FieldInfo for the member field with the enums name
            FieldInfo info = t.GetField(e.ToString("G"));

            // Check to see if the XmlEnumAttribute is defined on this field
            if (!info.IsDefined(typeof(XmlEnumAttribute), false))
            {
                // If no XmlEnumAttribute then return the string version of the enum.
                return e.ToString("G");
            }

            // Get the XmlEnumAttribute
            object[] o = info.GetCustomAttributes(typeof(XmlEnumAttribute), false);
            XmlEnumAttribute att = (XmlEnumAttribute)o[0];
            
            return att.Name;
        }

        public static Enum ConvertToEnum<T>(string field)
        {
            var values = GetEnumValues<T>();

            foreach (Enum e in values)
            {
                if (ConvertToString(e).Equals(field))
                {
                    return e;
                }
            }

            throw new ArgumentException("Field '" + field + "' is not a valid field of enum '" + typeof(T).Name + "'");
        }

        private static Enum[] GetEnumValues<T>()
        {
            var type = typeof(T);
            if (!type.IsEnum)
                throw new ArgumentException("Type '" + type.Name + "' is not an enum");

            return (
              from field in type.GetFields(BindingFlags.Public | BindingFlags.Static)
              where field.IsLiteral
              select (Enum)field.GetValue(type)).ToArray();
        }

        private static Array GetValues(Enum enumType)
        {
            Type type = enumType.GetType();

            FieldInfo[] fields = type.GetFields(BindingFlags.Public | BindingFlags.Static);

            Array array = Array.CreateInstance(type, fields.Length);

            for (int i = 0; i < fields.Length; i++)
            {
                var obj = fields[i].GetValue(null);
                array.SetValue(obj, i);
            }

            return array;
        }

        public static int IndexOf(string key, ItemsControl container)
        {
            int index = -1;
            for (int i = 0; i < container.Items.Count; i++)
            {
                if (((KeyValuePair<string, string>)container.Items[i]).Key.ToLower().Equals(key.ToLower()))
                {
                    index = i;
                    break;
                }
            }
            return index;
        }
        #endregion

        #region Authentication

        public static Login Login { get; set; }

        public static AuthenticationToken Token { get; set; }

        public static AuthenticationToken GenerateToken(string login, string password)
        {
            AuthenticationToken userToken = new AuthenticationToken();

            userToken.Login = Encrypt(login);
            userToken.Password = Encrypt(password);

            return userToken;
        }

        /// <summary>
        /// Chave AES
        /// </summary>
        private static byte[] Key { get; set; }
        /// <summary>
        /// Valores Iniciais do AES
        /// </summary>
        private static byte[] IV { get; set; }
        /// <summary>
        /// Converte uma string de valores hexadecimal para array de byte correspondente
        /// </summary>
        /// <param name="HexString">String de valores hexadecimal</param>
        /// <returns>Array de bytes correspondente a string</returns>
        private static byte[] ToByteArray(string HexString)
        {
            int NumberChars = HexString.Length;
            byte[] bytes = new byte[NumberChars / 2];
            for (int i = 0; i < NumberChars; i += 2)
            {
                bytes[i / 2] = Convert.ToByte(HexString.Substring(i, 2), 16);
            }
            return bytes;
        }
        /// <summary>
        /// Converte um array de bytes em uma string de valores hexadecimal
        /// </summary>
        /// <param name="bytes">Array de bytes</param>
        /// <returns>string de valores hexadecimal</returns>
        private static string ToString(byte[] bytes)
        {
            StringBuilder sb = new StringBuilder(bytes.Length * 2);
            foreach (byte b in bytes)
            {
                sb.AppendFormat("{0:x2}", b);
            }
            return sb.ToString();
        }
        /// <summary>
        /// Criptografa a string s usando AES
        /// </summary>
        /// <param name="s">String para criptografar</param>
        /// <returns>String criptografada</returns>
        private static string Encrypt(string plainText)
        {
            // Check arguments.
            if (plainText == null || plainText.Length <= 0)
                throw new ArgumentNullException("plainText");
            if (Key == null || Key.Length <= 0)
                throw new ArgumentNullException("Key");
            if (IV == null || IV.Length <= 0)
                throw new ArgumentNullException("IV");

            // Declare the streams used
            // to encrypt to an in memory
            // array of bytes.
            MemoryStream msEncrypt = null;
            CryptoStream csEncrypt = null;
            StreamWriter swEncrypt = null;

            // Declare the RijndaelManaged object
            // used to encrypt the data.
            AesManaged aesAlg = null;

            try
            {
                // Create a RijndaelManaged object
                // with the specified key and IV.
                aesAlg = new AesManaged();
                aesAlg.Key = Key;
                aesAlg.IV = IV;

                // Create a decrytor to perform the stream transform.
                ICryptoTransform encryptor = aesAlg.CreateEncryptor();

                // Create the streams used for encryption.
                msEncrypt = new MemoryStream();
                csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);
                swEncrypt = new StreamWriter(csEncrypt);

                //Write all data to the stream.
                swEncrypt.Write(plainText);

            }
            finally
            {
                // Clean things up.

                // Close the streams.
                if (swEncrypt != null)
                    swEncrypt.Close();
                if (csEncrypt != null)
                    csEncrypt.Close();
                if (msEncrypt != null)
                    msEncrypt.Close();

                // Clear the RijndaelManaged object.
                if (aesAlg != null)
                    aesAlg.Clear();
            }

            // Return the encrypted bytes from the memory stream.
            return ToString(msEncrypt.ToArray());

        }
        /// <summary>
        /// Construtor estatico
        /// </summary>
        static Util()
        {
            Key = ToByteArray("7624fd0c34c42da20512c0df0a321230");
            IV = ToByteArray("00000000000000000000000000000000");
        }
        #endregion
    }
}
